package com.lazypeople.task.impl;

import javafx.application.Platform;
import com.frostwire.jlibtorrent.*;
import com.frostwire.jlibtorrent.alerts.AddTorrentAlert;
import com.frostwire.jlibtorrent.alerts.Alert;
import com.frostwire.jlibtorrent.alerts.AlertType;
import com.frostwire.jlibtorrent.alerts.PieceFinishedAlert;
import com.frostwire.jlibtorrent.alerts.StateChangedAlert;
import com.frostwire.jlibtorrent.alerts.TorrentCheckedAlert;
import com.lazypeople.domain.DownloadStatus;
import com.lazypeople.domain.ProgressListener;
import com.lazypeople.domain.DownloadTask;

import java.io.File;
import java.util.concurrent.CountDownLatch;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class TorrentDownloadTask extends DownloadTask {
    private static final Logger logger = LoggerFactory.getLogger(TorrentDownloadTask.class);
    // 需实现 DownloadTask 接口的所有抽象方法，后续还需添加 addProgressListener 方法
    private final String torrentUrl;
    private final File saveDir;
    private final int threadCount;
    private volatile DownloadStatus status;
    private volatile long downloadedBytes;
    private volatile long totalBytes;
    private SessionManager sessionManager;
    private TorrentHandle torrentHandle;
    private volatile boolean paused;
    private volatile boolean cancelled;
    private final List<ProgressListener> listeners = new CopyOnWriteArrayList<>();

    public TorrentDownloadTask(String torrentUrl, File saveDir, int threadCount) {
        super(); // 显式调用父类构造方法
        this.torrentUrl = torrentUrl;
        this.saveDir = saveDir;
        this.threadCount = threadCount;
        this.status = DownloadStatus.READY;
        this.downloadedBytes = 0;
        this.totalBytes = 0;
        this.paused = false;
        this.cancelled = false;
    }

    @Override
    public void run() {
        status = DownloadStatus.DOWNLOADING;
        sessionManager = new SessionManager();
        try {
            appendLog("Starting torrent session...");
            SettingsPack settingsPack = new SettingsPack();
            // 设置下载的配置
            settingsPack.activeDownloads(threadCount);
            settingsPack.listenInterfaces("0.0.0.0:6881-6889,[::]:6881-6889");
            settingsPack.enableDht(true);
            SessionParams sessionParams = new SessionParams(settingsPack);

            sessionManager.start(sessionParams);

            // Configure session settings
            // SessionConfig config = sessionManager.getSessionConfig();
            // config.setUserAgent("JavaDownloadManager/1.0");
            // sessionManager.applyConfig(config);

            // Add torrent
            appendLog("Adding torrent: " + torrentUrl);
            final CountDownLatch latch = new CountDownLatch(1);

            // params.setUrl(torrentUrl);
            // params.setSavePath(saveDir.getAbsolutePath());
            // params.setAutoManaged(true);
            File torrentFile = null;
            // 检查URL类型，决定使用HTTP还是Torrent下载
            if (torrentUrl.startsWith("http://") || torrentUrl.startsWith("https://")) {
                // HTTP下载
                torrentFile = new File(torrentUrl);
            }

            if (torrentUrl.startsWith("magnet:?xt=urn:btih:")) {
                appendLog("Magnet link detected" + torrentUrl);
                startdl(torrentUrl, sessionManager);
                return;
            }
            final TorrentInfo torrentInfo = new TorrentInfo(torrentFile);
            sessionManager.download(torrentInfo, saveDir);

            sessionManager.addListener(new AlertListener() {
                @Override
                public int[] types() {
                    return new int[] { AlertType.ADD_TORRENT.swig() };
                }

                @Override
                public void alert(Alert<?> alert) {
                    if (alert instanceof AddTorrentAlert) {
                        AddTorrentAlert addTorrentAlert = (AddTorrentAlert) alert;
                        final TorrentInfo finalTorrentInfo = torrentInfo;
                        if (addTorrentAlert.handle().infoHash().equals(finalTorrentInfo.infoHash())) {
                            appendLog("Torrent added successfully: " + torrentInfo.name());
                            latch.countDown();
                        }
                    }
                }
            });
            sessionManager.addListener(new AlertListener() {
                @Override
                public int[] types() {
                    return new int[] { AlertType.TORRENT_ERROR.swig() };
                }

                @Override
                public void alert(Alert<?> alert) {
                    if (alert != null) {
                        appendLog("Failed to add torrent: " + torrentInfo.name());
                        status = DownloadStatus.FAILED;
                        latch.countDown();

                    }
                }
            });

            sessionManager.addListener(new AlertListener() {
                @Override
                public int[] types() {
                    return new int[] { AlertType.TORRENT_CHECKED.swig() };
                }

                @Override
                public void alert(Alert<?> alert) {
                    if (alert instanceof TorrentCheckedAlert) {
                        TorrentCheckedAlert torrentCheckedAlert = (TorrentCheckedAlert) alert;
                        if (torrentCheckedAlert.handle().infoHash().equals(torrentInfo.infoHash())) {
                            // 停止下载
                            sessionManager.stop();
                        }
                    }
                }
            });

            // Wait for torrent to be added
            latch.await();

            if (status == DownloadStatus.FAILED || torrentHandle == null) {
                appendLog("Aborting download due to error");
                return;
            }

            // Add alert listener
            sessionManager.addListener(new AlertListener() {
                @Override
                public int[] types() {
                    return new int[] { AlertType.STATE_CHANGED.swig() };
                }

                @Override
                public void alert(Alert<?> alert) {
                    handleAlert(alert);
                }
            });

            // Main download loop
            while (!cancelled && status != DownloadStatus.COMPLETED && status != DownloadStatus.FAILED) {
                if (paused) {
                    synchronized (this) {
                        status = DownloadStatus.PAUSED;
                        torrentHandle.pause();
                        appendLog("Download paused");
                        wait(); // Wait until resumed
                        torrentHandle.resume();
                        status = DownloadStatus.DOWNLOADING;
                        appendLog("Download resumed");
                    }
                }

                // Update download stats
                updateDownloadStats();

                // Check if download is complete
                if (torrentHandle.status().isFinished()) {
                    status = DownloadStatus.COMPLETED;
                    appendLog("Download completed successfully");
                    break;
                }

                // Small delay to reduce CPU usage
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }

        } catch (Exception e) {
            appendLog("Download error: " + e.getMessage());
            status = DownloadStatus.FAILED;
            e.printStackTrace();
        } finally {
            if (sessionManager != null) {
                sessionManager.stop();
                appendLog("Torrent session stopped");
            }
            // Ensure final UI update
            updateDownloadStats();
        }
    }

    private void handleAlert(Alert<?> alert) {
        if (alert.type() == AlertType.STATE_CHANGED) {
            StateChangedAlert scAlert = (StateChangedAlert) alert;
            appendLog("Torrent state changed: " + scAlert.getState());
        }
    }

    private void updateDownloadStats() {
        if (torrentHandle == null)
            return;

        TorrentStatus status = torrentHandle.status();
        downloadedBytes = status.totalDone();
        totalBytes = status.totalWanted();

        final double progress = totalBytes > 0 ? (double) downloadedBytes / totalBytes : 0;
        final String stats = String.format("Downloaded: %s / %s (%.2f%%)",
                formatSize(downloadedBytes), formatSize(totalBytes), progress * 100);

        Platform.runLater(() -> {
            // progressBar.setProgress(progress);
            appendLog(stats);
        });
    }

    private void appendLog(String message) {
        Platform.runLater(() -> {
            // logArea.appendText(message + "\n");
            // logArea.setScrollTop(Double.MAX_VALUE); // Auto-scroll to bottom
        });
    }

    private String formatSize(long bytes) {
        if (bytes < 1024)
            return bytes + " B";
        else if (bytes < 1048576)
            return String.format("%.1f KB", bytes / 1024.0);
        else if (bytes < 1073741824)
            return String.format("%.1f MB", bytes / 1048576.0);
        else
            return String.format("%.1f GB", bytes / 1073741824.0);
    }

    @Override
    public synchronized void pause() {
        if (status == DownloadStatus.DOWNLOADING) {
            paused = true;
        }
    }

    @Override
    public synchronized void resume() {
        if (status == DownloadStatus.PAUSED) {
            paused = false;
            notify(); // Wake up the download loop
        }
    }

    @Override
    public void cancel() {
        cancelled = true;
        status = DownloadStatus.CANCELLED;
        appendLog("Download cancelled");
    }

    @Override
    public DownloadStatus getStatus() {
        return status;
    }

    @Override
    public long getDownloadedBytes() {
        return downloadedBytes;
    }

    @Override
    public long getTotalBytes() {
        return totalBytes;
    }

    @Override
    public float getProgress() {
        return totalBytes > 0 ? (int) (downloadedBytes / totalBytes) : 0;
    }

    private static void waitForNodesInDHT(final SessionManager s) throws InterruptedException {
        final CountDownLatch signal = new CountDownLatch(1);

        final Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                long nodes = s.stats().dhtNodes();
                log("DHT contains " + nodes + " nodes");
                if (nodes >= 10) {
                    System.out.println("DHT contains " + nodes + " nodes");
                    signal.countDown();
                    timer.cancel();
                }
            }
        }, 0, 100000);

        System.out.println("Waiting for nodes in DHT (100 seconds)...");
        boolean r = signal.await(100, TimeUnit.SECONDS);
        if (!r) {
            System.out.println("DHT bootstrap timeout");
            System.exit(0);
        }
    }

    private static void log(String s) {
        logger.info(s);
    }

    private static void startdl(String magnetLink, SessionManager sm) throws InterruptedException {
        // Torrent dbTorrent = torrentService.findByTorrentName(name);
        String link = magnetLink;// dbTorrent.getPathToTorrent();
        File saveDir = new File("torrents/");
        if (!saveDir.exists()) {
            saveDir.mkdirs();
        }
        final SessionManager s = sm;// storrent.getSessionManager();
        AlertListener l = new AlertListener() {
            private int grade = 0;

            @Override
            public int[] types() {
                return new int[] {
                        AlertType.ADD_TORRENT.swig(),
                        AlertType.PIECE_FINISHED.swig(),
                        AlertType.TORRENT_FINISHED.swig(),
                        AlertType.TORRENT_ERROR.swig(),
                        AlertType.BLOCK_FINISHED.swig(),
                        AlertType.STATE_UPDATE.swig(),
                        AlertType.METADATA_RECEIVED.swig(),
                        AlertType.DHT_ERROR.swig()
                };
            }

            @Override
            public void alert(Alert<?> alert) {
                AlertType type = alert.type();
                switch (type) {
                    case ADD_TORRENT:
                        ((AddTorrentAlert) alert).handle().resume();
                        break;
                    case PIECE_FINISHED:
                        if (alert instanceof PieceFinishedAlert) {
                            int progress = (int) (((PieceFinishedAlert) alert).handle().status().progress() * 100);
                            if (grade < progress / 20) {
                                int index = (int) (((PieceFinishedAlert) alert).pieceIndex());
                                log("index: " + index);
                                grade += 1;
                                s.downloadRate();
                                log(progress + " %  downloaded");
                            }
                        }
                        System.out.println("PIECE_FINISHED");
                        break;
                    case TORRENT_FINISHED:
                        grade = 0;
                        // if (alert instanceof TorrentFinishedAlert) {
                        // TorrentFinishedAlert tfa = ((TorrentFinishedAlert) alert);
                        // tfa.handle().pause();
                        // }

                        System.out.println("TORRENT_FINISHED");
                        log("TORRENT_FINISHED");
                        break;
                    case TORRENT_ERROR:
                        // if (alert instanceof TorrentErrorAlert) {
                        // log(((TorrentErrorAlert) alert).what());
                        // log("is paused = " + ((TorrentErrorAlert) alert).handle().status());
                        // }
                        log("TORRENT_ERROR");
                        break;
                    case BLOCK_FINISHED:
                        // if (alert instanceof BlockFinishedAlert) {
                        // System.out.println("HERE: " + ((BlockFinishedAlert)
                        // alert).handle().status().progress());
                        // progress = (int) (((BlockFinishedAlert) alert).handle().status().progress() *
                        // 100);
                        // if (grade < progress / 20) {
                        // int index = (int) (((BlockFinishedAlert) alert).pieceIndex());
                        // log("index: " + index);
                        // grade += 1;
                        // s.downloadRate();
                        // log(progress + " % downloaded");
                        // }
                        // }
                        System.out.println("BLOCK_FINISHED");
                        log("BLOCK_FINISHED");
                        break;
                    case STATE_UPDATE:
                        // if (alert instanceof StateUpdateAlert) {
                        // log(((StateUpdateAlert) alert.type()).message());
                        // }
                        log(" STATE_UPDATE");
                        break;
                    case METADATA_RECEIVED:
                        log("metadata received");
                        break;
                    case DHT_ERROR:
                        // if (alert instanceof DhtErrorAlert) {
                        // log("dht error");
                        // log(((DhtErrorAlert) alert).message());
                        // }
                        log("dht error");
                        break;
                    default:
                        break;
                }
            }
        };
        s.addListener(l);
        if (s.isRunning() != true)
            s.start();
        if (link.startsWith("magnet:?")) {
            waitForNodesInDHT(s);
            byte[] data = s.fetchMagnet(link, 30);
            TorrentInfo ti = TorrentInfo.bdecode(data);
            log(Entry.bdecode(data).toString());
            log("is valid ? =" + ti.isValid());
            s.download(ti, saveDir);
            log("torrent added with name = " + ti.name());
            // storrent.addTh(s.find(ti.infoHash()), name);
            log(s.find(ti.infoHash()).isValid() + " isvalid");
            log("torrent added to session");
            // this.videoname = ti.name();
            int i = 0;
            while (i < 20) {
                TimeUnit.SECONDS.sleep(1);
                log(s.find(ti.infoHash()).status().state() + " state");
                log(s.find(ti.infoHash()).status().progress() * 100 + " progress");
                i++;
            }
        }
    }

    @Override
    public void addProgressListener(ProgressListener listener) {
        listeners.add(listener);
    }

    @Override
    public void removeProgressListener(ProgressListener listener) {
        // 实现进度监听器移除逻辑
        listeners.remove(listener);
    }

    private void notifyProgressChanged() {
        Platform.runLater(() -> {
            for (ProgressListener listener : listeners) {
                listener.onProgressChanged(getProgress(), downloadedBytes, totalBytes);
            }
        });
    }
}
